{# One voice profile's editor, in three steps (``v.steps``): Voice, who it is; Delivery, how it talks; Review, a last look, heard and saved. Drawn from the designer's view as it comes. The whole design rides the one form from step to step (``v.form``: what a step does not show it carries hidden), and the form is drawn again as it changes. #} {% from "components/macros/chat.html" import speak_button %} {% from "components/macros/layout.html" import avatar %} {% from "components/macros/form.html" import checklist, field, primary_button, range_chips, search_input, textarea %} {% set v = view %} {% set d = v.delivery %} {% macro carry(shown) %}{% for name, value in v.form.items() if name not in shown %}{% endfor %}{% endmacro %} {# The chosen voice, pinned: above the Voice step's search (as its picked radio), and atop Delivery (with the model it is spoken on). #} {% macro chosen_bar(pick=False) %} {% if v.chosen %}
{% if pick %}{% else %}Voice{% endif %} {{ avatar(v.chosen.provider_name, marks.get(v.chosen.provider)) }} {{ v.chosen.name }} {{ v.chosen.detail }}{% if not pick %} ยท on {{ v.model }}{% endif %} {{ speak_button(chosen_listen, "text-aegis-muted hover:text-aegis-text") }}
{% endif %} {% endmacro %} {% macro chips(label, choices, value, name) %}
{{ label }}{{ range_chips(choices, value, field=name, aria=label) }}
{% endmacro %}
โ† Profiles {{ v.profile.name }} {% if v.active %}In use{% endif %}
{% if step == "voice" %} {% set f = v.find %} {{ carry(["model"] + (["voice"] if v.chosen else [])) }}
{{ chosen_bar(pick=True) }}
{{ search_input("q", f.q, placeholder="Search voices") }} {{ checklist("ai-voice-provider", "provider", "Provider", f.options.provider) }} {{ checklist("ai-voice-gender", "gender", "Gender", f.options.gender) }} {{ checklist("ai-voice-tone", "family", "Tone", f.options.family) }} {{ checklist(accent_id, "accent_of", "Accent", f.options.accent) }}
{% for l in v.locked %}

{{ l.voices }} {{ l.provider_name }} voices need {{ l.key }}: add it in Secrets to use them.

{% endfor %}
{% for c in f.cards %} {% else %}

No other voice matches all of those: clear a filter or the search.

{% endfor %}
{% if f.matched > f.cards|length %}

The first {{ f.cards|length }} of {{ "{:,}".format(f.matched) }}; search or narrow to see the rest.

{% endif %}

{{ v.chosen.name if v.chosen else v.voice }} is spoken on

{% for m in v.models %} {% endfor %}
{% elif step == "delivery" %} {{ carry(["style", "extra"] + v.dials.keys()|list + (["speed"] if v.speed_range else [])) }} {{ chosen_bar() }} {% if not v.follows %}

{{ v.model }} ignores delivery instructions: a style and fine-tuning change nothing. Pick a model that follows them on the Voice step.

{% endif %}
{% for key, s in v.styles.items() %} {% endfor %}
{% if v.speed_range %}
Speed {{ v.speed }}
{% endif %}
Fine-tune (hints the model follows loosely; accents most of all)
{% for name, choices in v.dials.items() %}{{ chips(name|title, choices.items()|list, d[name], name) }}{% endfor %} {% if not v.steers_accent %}

On {{ v.model }} the accent comes with the voice{% if v.chosen and v.chosen.accent %} ({{ v.chosen.name }}: {{ v.chosen.accent }}){% endif %}: pick a voice by accent on the Voice step.

{% endif %} {% call field("Anything else (pronunciation, words to avoid, how to open)") %}{{ textarea("extra", d.extra or "", rows=3) }}{% endcall %}
{% else %} {{ carry([]) }}
Voice
{{ v.chosen.name if v.chosen else v.voice }}{% if v.chosen %} {{ v.chosen.detail }}{% endif %}
Model
{{ v.model }}{% if not v.follows %} (ignores delivery instructions){% endif %}
Style
{{ v.styles[d.style or ""].label }}
{% if v.speed_range %}
Speed
{{ v.speed }}
{% endif %}

What gets sent

{{ v.instructions }}
{{ speak_button(listen_url, "text-aegis-muted hover:text-aegis-text", form=True) }} Listen {{ primary_button("Save", attrs='data-write data-voice-save hx-post="' ~ save_url ~ '" hx-include="closest form"') }}
{% endif %}
{% set keys = v.steps.keys()|list %}{% set at = keys.index(step) %} {% if at > 0 %}{% endif %} {% if at < keys|length - 1 %}{% endif %}